﻿//========================================================= 
/**@file WinHelper.h 
 * @brief Windows环境下的一些辅助接口函数
 * 
 * @date 2013-07-31 21:58:36 
 * @author Zhyioo 
 * @version 1.0
 */ 
//--------------------------------------------------------- 
#ifndef _LIBZHOUYB_WINHELPER_H_
#define _LIBZHOUYB_WINHELPER_H_
//--------------------------------------------------------- 
#include "WinRegistryKey.h"
using zhou_yb::extension::ability::RegistryKey;

#include "HWndHelper.h"
#include "MouseMirror.h"
#include "KeyboardMirror.h"

#include <process.h>
#include <Psapi.h>

#include <shlobj.h>
#pragma comment(lib, "Shell32.lib")
//--------------------------------------------------------- 
namespace zhou_yb {
namespace extension {
namespace ability {
//--------------------------------------------------------- 
/// Windows下常用功能 
class WinHelper
{
protected:
    WinHelper() {}

    static BOOL CALLBACK EnumWindowCallback(HWND hWnd, LPARAM lParam)
    {
        if(!HWndHelper::IsMainHwnd(hWnd))
            return TRUE;

        typedef std::pair<std::pair<list_t<DWORD>, ByteArray>, list_t<HWND> > ParamType;
        ParamType* p = ctype_cast(ParamType*)lParam;
        DWORD pId = HWndHelper::GetProcessID(hWnd);
        if(!list_helper<DWORD>::contains(p->first.first, pId))
            return TRUE;

        const size_t MAX_BUFF = 256;
        TCHAR textBuff[MAX_BUFF] = { 0 };
        int len = GetWindowText(hWnd, textBuff, MAX_BUFF);
        CharConverter cvt;
        if(len > 0 && StringConvert::Contains(cvt.to_char(textBuff, len), p->first.second))
        {
            p->second.push_back(hWnd);
        }
        return TRUE;
    }
public:
    //----------------------------------------------------- 
    /// 获取系统路径(默认为桌面的路径)
    static const char* GetSystemPath(UINT pathType = CSIDL_DESKTOP)
    {
        static char_t dirPath[_MAX_PATH] = {0};
        static CharConverter cvt;
        SHGetSpecialFolderPath(NULL, dirPath, pathType, 0);
        return cvt.to_char(dirPath);
    }
    /// 获取模块完整路径名称 
    static const char* GetModulePath(const char* moduleName = NULL)
    {
        static char_t path[_MAX_PATH] = { 0 };
        static CharConverter cvt;

        HMODULE hModule = NULL;
        if(!_is_empty_or_null(moduleName))
        {
            hModule = GetModuleHandle(cvt.to_char_t(moduleName));
            if(hModule == NULL)
                return "";
        }

        size_t len = static_cast<size_t>(GetModuleFileName(hModule, path, _MAX_PATH));
        if(len < 1)
            return "";
        return cvt.to_char(path);
    }
    /**
     * @brief 获取相对路径的完整路径
     * @date 2018-12-10 14:59
     */
    static size_t GetFullPath(const char* path, ByteBuilder& fullPath)
    {
        CharConverter cvt;
        char_t buff[_MAX_PATH] = { 0 };
        char_t* lppPart = { NULL };
        DWORD len = GetFullPathName(cvt.to_char_t(path), _MAX_PATH, buff, &lppPart);
        fullPath.Append(cvt.to_char(buff, len));
        return static_cast<size_t>(len);
    }
    /**
     * @brief 提取路径中的文件名
     * @date 2017-03-18 11:33
     * 
     * @param [in] path 需要解析的路径
     * @param [out] pDir 截取到的路径
     * @param [out] pName 截取到的文件名
     * @param [out] pExt 截取到的文件扩展名
     */
    static bool ExtractFileName(const ByteArray& path, ByteBuilder* pDir, ByteBuilder* pName, ByteBuilder* pExt)
    {
        // 查找 . 和 / 号
        if(path.IsEmpty())
            return false;

        size_t dotIndex = StringConvert::LastIndexOf(path, '.');
        size_t splitIndex = StringConvert::LastIndexOfAny(path, "/\\");
        // 没有文件名
        if((splitIndex != SIZE_EOF && dotIndex < splitIndex) || (dotIndex == SIZE_EOF))
        {
            size_t len = path.GetLength();
            while(path[len - 1] == '\\' || path[len - 1] == '/')
            {
                --len;
                if(len < 1)
                    break;
            }
            pDir->Append(path.SubArray(0, len));
            return true;
        }
        if(pDir != NULL && splitIndex != SIZE_EOF)
        {
            pDir->Append(path.SubArray(0, splitIndex));
        }
        if(pExt != NULL)
        {
            pExt->Append(path.SubArray(dotIndex + 1));
        }
        if(pName != NULL)
        {
            ByteArray name(path);
            if(splitIndex != SIZE_EOF)
            {
                name = path.SubArray(splitIndex + 1);
                dotIndex -= splitIndex;
                dotIndex -= 1;
            }
            name = name.SubArray(0, dotIndex);
            pName->Append(name);
        }
        return true;
    }
    /**
     * @brief 获取模块目录路径
     * 
     * @param [in] moduleName 需要获取的模块目录路径,路径后面不带 '\' 
     * 
     * @return const char* ""表示获取失败 
     */
    static const char* GetModuleDirectory(const char* moduleName = NULL)
    {
        const char* path = GetModulePath(moduleName);
        size_t len = strlen(path);
        if (len > 0)
        {
            while (path[len] != L'\\')
                --len;
            char* p = const_cast<char*>(path);
            p[len] = 0;
        }
        else
        {
            path = ".";
        }

        return path;
    }
    /// 判断系统版本 
    static bool IsWinVersion(DWORD majorVer, DWORD minorVer)
    {
        OSVERSIONINFOEX osvi;
        DWORDLONG dwlConditionMask = 0;

        ZeroMemory(&osvi, sizeof(osvi));
        osvi.dwOSVersionInfoSize = sizeof(osvi);
        osvi.dwMajorVersion = majorVer;
        osvi.dwMinorVersion = minorVer;

        VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_EQUAL);
        VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_EQUAL);
        return Tobool(VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION, dwlConditionMask));
    }
    /// 枚举当前目录下的文件和文件夹信息,如果需要枚举多层的,手动处理递归  
    static size_t EnumFiles(const char* rootFolder, list_t<string>* pFiles, list_t<string>* pFolders, const char* ext = "*.*")
    {
        size_t count = 0;
        if(pFolders == NULL && pFiles == NULL)
            return count;

        CharConverter cvt;
        string file;
        file += rootFolder;
        file += "\\"; 
        file += ext;

        WIN32_FIND_DATA wfd; 
        HANDLE hFind = FindFirstFile(cvt.to_char_t(file.c_str()), &wfd);
        if (hFind == INVALID_HANDLE_VALUE)  
            return count;

        do
        {
            if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
            {
                if(pFolders != NULL)
                {
                    (*pFolders).push_back("");
                    string& str = (*pFiles).back();
                    str += rootFolder;
                    str += "\\";
                    str += cvt.to_char(wfd.cFileName);

                    ++count;
                }

                continue;
            }

            if(pFiles != NULL)
            {
                (*pFiles).push_back("");
                string& str = (*pFiles).back();
                str += rootFolder;
                str += "\\";
                str += cvt.to_char(wfd.cFileName);           

                ++count;
            }
        } while(FindNextFile(hFind, &wfd));
        FindClose(hFind);

        return count;
    }
    /// 复制字符串到剪贴板 
    static bool CopyTextToClipboard(HWND hWnd, const char* text)
    {
        BOOL result;
        result = OpenClipboard(hWnd);
        if(!result)
            return false;

        EmptyClipboard();
        
        size_t len = _strlen(text);
        if(len > 0)
        {
            HANDLE hClip = NULL;
            char* pBuf = NULL;

            hClip = GlobalAlloc(GMEM_MOVEABLE, len + 1);
            pBuf = reinterpret_cast<char*>(GlobalLock(hClip));

            memcpy(pBuf, text, len);
            pBuf[len] = 0;

            GlobalUnlock(hClip);
            SetClipboardData(CF_TEXT, hClip);
        }
        CloseClipboard();
        
        return true;
    }
    /// 模拟按键 
    static bool KeyEventText(const char* str, HWND hWnd = NULL)
    {
        if(hWnd == NULL)
        {
            hWnd = GetForegroundWindow();
        }

        AttachInputHWnd attachHwnd(hWnd);
        if(!attachHwnd.IsValid())
            return false;

        hWnd = GetFocus();
        if(INVALID_HANDLE_VALUE == hWnd)
            return false;

        size_t len = _strlen(str);
        for(size_t i = 0;i < len; ++i)
        {
            KeyboardMirror::typing(str[i]);
        }
        return true;
    }
    /// 设置程序开机自启动 
    static bool ExeAutoRun(const char* exePath, bool isAutoRun = true)
    {
        size_t len = _strlen(exePath);
        RegistryKey rootKey = RegistryKey::LocalMachine.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Run", KEY_READ | KEY_WRITE);
        ByteBuilder appName(8);

        // 传入的路径为空则自动获取当前程序 
        if(len < 1)
        {
            exePath = GetModulePath();
            len = strlen(exePath);
            if(len < 1)
                return false;
        }

        size_t index = StringConvert::LastIndexOf(ByteArray(exePath, len), '\\');
        if(index == SIZE_EOF)
            index = 0;
        appName = exePath + index + 1;

        // 过滤 ".exe" 后缀 
        index = StringConvert::LastIndexOf(appName, '.');
        if(index >= 0)
        {
            len = appName.GetLength() - index;
            appName.RemoveTail(len);
        }

        bool bRet = false;
        if(isAutoRun)
        {
            bRet = rootKey.SetValue(appName.GetString(), exePath);
        }
        else
        {
            // 删除注册表键值 
            bRet = rootKey.DeleteValue(appName.GetString());
        }
        return bRet;
    }
    /// 重启电脑
    static bool ExitWindow(DWORD dwMode = EWX_SHUTDOWN)
    {
        HANDLE hToken;
        TOKEN_PRIVILEGES tkp;
        LUID uid;
        HANDLE hProcess = GetCurrentProcess();
        if(!OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
            return false;
        CharConverter cvt;
        if(!LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &uid) && GetLastError() != ERROR_SUCCESS)
        {
            CloseHandle(hToken);
            return false;
        }
        tkp.PrivilegeCount = 1;
        tkp.Privileges[0].Luid = uid;
        tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
        
        if(!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), 
            ctype_cast(PTOKEN_PRIVILEGES)NULL, NULL) 
            && GetLastError() != ERROR_SUCCESS)
        {
            CloseHandle(hToken);
            return false;
        }
        CloseHandle(hToken);
        if(!(::ExitWindowsEx(dwMode, 0)) && GetLastError() != ERROR_SUCCESS)
            return false;
        return true;
    }
    //----------------------------------------------------- 
    /**
     * @brief 根据进程名称获取进程ID
     * @date 2018-08-27 09:38
     */
    static DWORD GetProcessID(const char* processName, bool isIgnoreCase = false)
    {
        DWORD iProcID = 0;
        PROCESSENTRY32 pEntry = { sizeof(PROCESSENTRY32) };
        CharConverter cvt;
        ByteArray name(processName);

        HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
        if(hSnapshot != INVALID_HANDLE_VALUE)
        {
            if(Process32First(hSnapshot, &pEntry))
            {
                while(Process32Next(hSnapshot, &pEntry))
                {
                    if(StringConvert::Compare(name, cvt.to_char(pEntry.szExeFile), isIgnoreCase))
                    {
                        iProcID = pEntry.th32ProcessID;
                        break;
                    }
                }
            }
            CloseHandle(hSnapshot);
        }
        return iProcID;
    }
    /**
     * @brief 获取进程句柄
     * @date 2018-09-10 11:28
     */
    static HANDLE GetProcessHandle(const char* processName, bool isIgnoreCase = false)
    {
        PROCESSENTRY32 pEntry = { sizeof(PROCESSENTRY32) };
        CharConverter cvt;
        ByteArray name(processName);

        HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
        HANDLE hProcess = NULL;
        if(hSnapshot != INVALID_HANDLE_VALUE)
        {
            if(Process32First(hSnapshot, &pEntry))
            {
                while(Process32Next(hSnapshot, &pEntry))
                {
                    if(StringConvert::Compare(name, cvt.to_char(pEntry.szExeFile), isIgnoreCase))
                    {
                        hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, pEntry.th32ProcessID);
                        break;
                    }
                }
            }
            CloseHandle(hSnapshot);
        }
        return hProcess;
    }
    //----------------------------------------------------- 
    /**
     * @brief 枚举指定进程的主窗口
     * @date 2018-11-04 10:15
     * 
     * @param [in] processName 需要查找的进程完整名称
     * @param [in] caption 需要部分匹配的窗口标题
     * @param [out] hWnds 获取到的窗口句柄
     * 
     * @return size_t 获取到的窗口数
     */
    static size_t EnumHwnd(const char* processName, const char* caption, list_t<HWND>& hWnds)
    {
        ByteArray name(processName);
        ByteArray captionBuff(caption);
        // 枚举进程
        PROCESSENTRY32 pEntry = { sizeof(PROCESSENTRY32) };
        CharConverter cvt;
        std::pair<std::pair<list_t<DWORD>, ByteArray>, list_t<HWND> > param;
        list_t<DWORD>& pIdList = param.first.first;
        HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
        HANDLE hProcess = NULL;
        if(hSnapshot != INVALID_HANDLE_VALUE)
        {
            if(Process32First(hSnapshot, &pEntry))
            {
                while(Process32Next(hSnapshot, &pEntry))
                {
                    if(name.IsEmpty() || StringConvert::Compare(name, cvt.to_char(pEntry.szExeFile)))
                    {
                        pIdList.push_back(pEntry.th32ProcessID);
                    }
                }
            }
            CloseHandle(hSnapshot);
        }
        // 没有找到进程
        if(pIdList.size() < 1)
            return 0;
        // 无法直接使用PID获取到的HWND读取到标题内容,需要手动枚举窗口获取
        param.first.second = captionBuff;
        EnumWindows(EnumWindowCallback, ctype_cast(LPARAM)(&param));
        hWnds += param.second;
        return param.second.size();
    }
    //----------------------------------------------------- 
};
//--------------------------------------------------------- 
} // namespace ability 
} // namespace extension 
} // namespace zhou_yb
//--------------------------------------------------------- 
#endif // _LIBZHOUYB_WINHELPER_H_
//========================================================= 
